using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace pong1
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class PongPaddle : Microsoft.Xna.Framework.DrawableGameComponent
    {

        Vector2 position;           // Current location of paddle = top left corner
        Vector2 velocity;           // increments by which the paddle can move
        Texture2D paddleTexture2D;      // Image used for the paddle
        SpriteBatch spriteBatch;    // Stores reference to spritebatch retrieved from Services object
        String strEventReceiver;          // Event Receiver name of this paddle 
        String strPaddleName;          // name of the paddle for loading texture [CONFIGURATION VARIABLE]
        Resources.PADDLESIDE paddleSide;
        Resources.PLAYER player;          // Tells whether this paddle is player1 or player2
        EventCollection eventCollection;    // Stores reference to pending events list
        String[] eventsForThisGameObjectArray;  // Stores a list of events that this object polls for
        bool bRotatePaddle;         // Tells whether the paddle is to be rotated or not
        float fPaddleAngle;         // Tells the angle by which the paddle must rotate
        float fPaddleRotatedAccumulator;    // Controls how long the paddle will remain rotated == Resources.MAX_PADDLE_ROTATE_TIME 

        public PongPaddle(Game game, Resources.PADDLESIDE side)
            : base(game)
        {
            paddleSide =  side;
            if (paddleSide == Resources.PADDLESIDE.RIGHTPADDLE)
            {
                player = Resources.PLAYER.PLAYER1;
                strEventReceiver = "paddle1";
                strPaddleName = "paddle1";
            }
            else
            {
                player = Resources.PLAYER.PLAYER2;
                strEventReceiver = "paddle2";
                strPaddleName = "paddle2";

            }

            bRotatePaddle = false;
            fPaddleAngle = 0;
            fPaddleRotatedAccumulator = 0;
            velocity.Y = Resources.PADDLE_POSITION_INCREMENT;
            eventCollection = (EventCollection)Game.Services.GetService(typeof(EventCollection));
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  
        /// 1. Load paddle texture
        /// 2. Set initial positions of paddles
        /// </summary>
        public override void Initialize()
        {
            paddleTexture2D = Game.Content.Load<Texture2D>(strPaddleName);
            base.Initialize();
        }

        /// <summary>
        /// Gets references to the spriteBatch, eventCollection and set of events it's listening for
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            if (paddleSide == Resources.PADDLESIDE.LEFTPADDLE)
            {
                position.X = paddleTexture2D.Width / 2; ;
                position.Y = GraphicsDevice.Viewport.Height / 2 - paddleTexture2D.Height / 2; ;
            }
            else
            {
                position.X = GraphicsDevice.Viewport.Width - paddleTexture2D.Width - paddleTexture2D.Width / 2; ;
                position.Y = GraphicsDevice.Viewport.Height / 2 - paddleTexture2D.Height / 2; ;
            }
            putPositionOnEventCollection();

            String eventsForThisPaddle;
            eventsForThisPaddle = eventCollection.GetEventsForReceiver(strEventReceiver);
 
            eventsForThisGameObjectArray = eventsForThisPaddle.Split(',');
            base.LoadContent();
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// Paddle will process it's relevant events
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // Paddle processes it's relevant events
            // 
            if (!eventCollection.IsEmpty())
            {
                Event e;
                for (int j = 0; j < eventsForThisGameObjectArray.Length; j++)       // For each event this paddle is listening for, retrieve and process the event
                {
                    e = eventCollection.RetrieveEvent(eventsForThisGameObjectArray[j]);

                    if (e != null)
                    {
                        processEvent(e);
                    }
                }
                
            }
            // Paddle needs to publish its new position by placing it on the events Collection
            putPositionOnEventCollection();

            // Paddle needs to publish its velocity (which is zero for an un-flicked paddle)
            putVelocityOnEventCollection();

            // If the paddle is rotated, check whether it's time to put the paddle back to its normal position
            if ( (bRotatePaddle == true)  && fPaddleRotatedAccumulator < Resources.MAX_PADDLE_ROTATE_TIME )
            {
                fPaddleRotatedAccumulator += (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// The event processor for all events handled by PongPaddle
        /// 
        /// </summary>
        /// <param name="ea"></param>
        private void processEvent(Event e) 
        {

            switch (e.EventName)
            {
                case "uiarrows":        // Handling input from keyboard from user(s)
                case "uiwsad":
                    switch (e.EventArguments[0].ArgumentName )
                    {
                        case "move":
                            switch (e.EventArguments[0].ArgumentValue)
                            {
                                case "up":
                                    if (!((int)position.Y <= 0)) // Paddle has hit top of screen
                                    {
                                        position.Y -= velocity.Y;
                                    }
                                    else
                                    {
                                        position.Y = 0;
                                    }
                                    break;
                                case "down":
                                    if (! ( ( (int)position.Y + paddleTexture2D.Height) > GraphicsDevice.Viewport.Height))// Paddle has hit bottom of screen
                                    {
                                        position.Y += velocity.Y;
                                    }

                                    break;
                                case "left":
                                    // Rotate paddle so it 'flicks' the ball
                                    bRotatePaddle = true;
                                    if (paddleSide == Resources.PADDLESIDE.RIGHTPADDLE)
                                        fPaddleAngle = MathHelper.ToRadians(Resources.PADDLE_ROTATE_ANGLE);
                                    else
                                        fPaddleAngle = MathHelper.ToRadians(-1 * Resources.PADDLE_ROTATE_ANGLE);
                                    break;
                                case "right":
                                    // Rotate paddle so it 'flicks' the ball
                                    bRotatePaddle = true;
                                    if (paddleSide == Resources.PADDLESIDE.RIGHTPADDLE)
                                        fPaddleAngle = MathHelper.ToRadians(-1 * Resources.PADDLE_ROTATE_ANGLE);
                                    else
                                        fPaddleAngle = MathHelper.ToRadians(Resources.PADDLE_ROTATE_ANGLE);
                                    break;
                                default:
                                    break;
                            }
                            break;
                        default:
                            break;
                    }
                    

                    break;
                case "uimouse":         // Handling input from mouse from user
                    switch (e.EventArguments[0].ArgumentName )
                    {
                        case "yvalue":
                                position.Y = Convert.ToInt32(e.EventArguments[0].ArgumentValue);
                                // Checks to make sure paddledoesn't go off screen
                                if (position.Y < 0)
                                    position.Y = 0;

                                if (position.Y + paddleTexture2D.Height > GraphicsDevice.Viewport.Height)
                                    position.Y = GraphicsDevice.Viewport.Height - paddleTexture2D.Height;

                                break;
                        case "mbpressed":
                            switch (e.EventArguments[0].ArgumentValue)
                            {
                                case "left":
                                    // Rotate paddle so it 'flicks' the ball
                                    bRotatePaddle = true;
                                    if (paddleSide ==  Resources.PADDLESIDE.RIGHTPADDLE)
                                        fPaddleAngle = MathHelper.ToRadians(Resources.PADDLE_ROTATE_ANGLE);
                                    else
                                        fPaddleAngle = MathHelper.ToRadians(-1 * Resources.PADDLE_ROTATE_ANGLE);
                                    break;
                                case "right":
                                    // Rotate paddle so it 'flicks' the ball
                                    bRotatePaddle = true;
                                    if (paddleSide ==  Resources.PADDLESIDE.RIGHTPADDLE)
                                        fPaddleAngle = MathHelper.ToRadians(-1 * Resources.PADDLE_ROTATE_ANGLE);
                                    else
                                        fPaddleAngle = MathHelper.ToRadians(Resources.PADDLE_ROTATE_ANGLE);
                                    break;
                                default:
                                    break;
                            }
                            break;
                        default:
                            break;
                    }
                    
                    break;
                case "sim1":    // Simulation for player
                case "sim2":
                    Event e3 = eventCollection.SearchEvent("balltrajectory");
                    if (e3 == null)
                        return;
                    int Y = Convert.ToInt32(e3.EventArguments[1].ArgumentValue);
                    int X = Convert.ToInt32(e3.EventArguments[0].ArgumentValue);
                    if (X == 0 && paddleSide == Resources.PADDLESIDE.LEFTPADDLE)
                    {
                        if (Y >= position.Y && Y <= position.Y + paddleTexture2D.Height)
                        { }
                        else
                            if (Y < position.Y)
                        {
                            if (!((int)position.Y <= 0))
                                position.Y -= velocity.Y;
                            else
                                position.Y = 0;     // Stop paddle from going of screen
                        }
                        else
                        {
                            if (!(((int)position.Y + paddleTexture2D.Height) > GraphicsDevice.Viewport.Height))// Paddle has hit bottom of screen
                                position.Y += velocity.Y;
                        }
                    }
                    else if (X != 0 && paddleSide == Resources.PADDLESIDE.RIGHTPADDLE)
                    {
                        if (Y >= position.Y && Y <= position.Y + paddleTexture2D.Height)
                        { }
                        else
                        if (Y < position.Y)
                        {
                            if (!((int)position.Y <= 0))
                                position.Y -= velocity.Y;
                            else
                                position.Y = 0;     // Stop paddle from going of screen
                        }
                        else
                        {
                            if (!(((int)position.Y + paddleTexture2D.Height) > GraphicsDevice.Viewport.Height))// Paddle has hit bottom of screen
                                position.Y += velocity.Y;
                        }
                    
                    }

                    break;
                default:
                    break;
            }
        }
        
        /// <summary>
        /// Creates event containing paddle velocity and places it in eventCollection
        /// </summary>
        private void putVelocityOnEventCollection()
        {
            eventCollection.RetrieveEvent(strEventReceiver + "velocity");   // Remove the old such event if it's still on the EventCollection

            Event e2 = new Event();
            e2.EventName = strEventReceiver + "velocity";
            e2.EventArguments[0].ArgumentName = "x";
            e2.EventArguments[0].ArgumentType = "int";
            e2.EventArguments[0].ArgumentValue = position.X.ToString();

            e2.EventArguments[1].ArgumentName  = "y";
            e2.EventArguments[1].ArgumentType = "int";
            e2.EventArguments[1].ArgumentValue = position.Y.ToString();

            e2.EventArguments[2].ArgumentName  = "velocityx";
            e2.EventArguments[2].ArgumentType = "int";
            e2.EventArguments[2].ArgumentValue = velocity.X.ToString();

            e2.EventArguments[3].ArgumentName = "velocityy";
            e2.EventArguments[3].ArgumentType = "int";
            e2.EventArguments[3].ArgumentValue = velocity.Y.ToString();

            eventCollection.Add(e2);
        }

        /// <summary>
        /// Creates event containing paddle position and places it in eventCollection
        /// </summary>
        private void putPositionOnEventCollection()
        {
            eventCollection.RetrieveEvent(strEventReceiver + "position");   // Remove the old such event if it's still on the EventCollection
            Event e1 = new Event();
            e1.EventName = strEventReceiver + "position";
            e1.EventArguments[0].ArgumentName = "x";
            e1.EventArguments[0].ArgumentType = "int";
            e1.EventArguments[0].ArgumentValue = position.X.ToString();

            e1.EventArguments[1].ArgumentName = "y";
            e1.EventArguments[1].ArgumentType = "int";
            e1.EventArguments[1].ArgumentValue = position.Y.ToString();

            e1.EventArguments[2].ArgumentName = "width";
            e1.EventArguments[2].ArgumentType = "int";
            e1.EventArguments[2].ArgumentValue = paddleTexture2D.Width.ToString();

            e1.EventArguments[3].ArgumentName = "height";
            e1.EventArguments[3].ArgumentType = "int";
            e1.EventArguments[3].ArgumentValue = paddleTexture2D.Height.ToString();

            eventCollection.Add(e1);

        
        }
        
        public override void Draw(GameTime gameTime)
        {
            spriteBatch = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            spriteBatch.Begin();

            if (bRotatePaddle)
            {
                spriteBatch.Draw(paddleTexture2D, new Vector2(position.X + paddleTexture2D.Width/2, position.Y  + paddleTexture2D.Height/2), null, Color.White, fPaddleAngle, new Vector2(paddleTexture2D.Width/2, paddleTexture2D.Height/2), 1.0f, SpriteEffects.None, 1.0f);
                // if(time elapsed since paddle roatation >= half second then)
                if( fPaddleRotatedAccumulator >= 0.5f)
                {
                    bRotatePaddle = false;
                    fPaddleRotatedAccumulator = 0;
                }
            }
            else 
            {

                spriteBatch.Draw(paddleTexture2D, position, Color.White);
            }
            
            spriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// PongPaddle is the Observer for the variable 'GameDescriptionSet' in UserInput class
        /// When the game description becomes known, PongPaddle registers for its relevant events
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void GameDescriptionIsSet(object sender, GameDescriptionSetEventArgs e) 
        { 
            switch (e.egameDescription)
	        {
                case Resources.GAMEDESCRIPTION.ONE_PLAYER_KEYBOARD: // Player1 register for 'uiarrows' event, player2 register for 'sim2' event
                    if(player == Resources.PLAYER.PLAYER1)
                        eventCollection.RegisterForEvent("uiarrows", strEventReceiver);

                    if(player == Resources.PLAYER.PLAYER2)
                        eventCollection.RegisterForEvent("sim2", strEventReceiver);    
                break;

                case Resources.GAMEDESCRIPTION.ONE_PLAYER_MOUSE:    // Player1 register for 'uimouse' event, player2 register for 'sim2' event
                    if(player == Resources.PLAYER.PLAYER1)
                        eventCollection.RegisterForEvent("uimouse", strEventReceiver);     
                    
                    if(player == Resources.PLAYER.PLAYER2)
                        eventCollection.RegisterForEvent("sim2", strEventReceiver);    

                    break;
                
                case Resources.GAMEDESCRIPTION.TWO_PLAYERS_KEYBOARD:    // Player1 register for 'uiarrows' event, player2 register for 'uiwsad' event
                    if(player == Resources.PLAYER.PLAYER1)
                        eventCollection.RegisterForEvent("uiarrows", strEventReceiver);        
                    
                    if (player == Resources.PLAYER.PLAYER2)
                        eventCollection.RegisterForEvent("uiwsad", strEventReceiver);        

                    break;
                
                case Resources.GAMEDESCRIPTION.SIMULATION:          // Player1 register for 'sim1' event, player2 register for 'sim2' event
                    if(player == Resources.PLAYER.PLAYER1)
                        eventCollection.RegisterForEvent("sim1", strEventReceiver);        
                    
                    if (player == Resources.PLAYER.PLAYER2)
                        eventCollection.RegisterForEvent("sim2", strEventReceiver);        
                    break;

                case Resources.GAMEDESCRIPTION.TWO_PLAYERS_KEYBOARD_MOUSE:  // Player1 register for 'uiarrows' event, player2 register for 'uimouse' event
                    if(player == Resources.PLAYER.PLAYER1)
                        eventCollection.RegisterForEvent("uiarrows", strEventReceiver);
                    
                    if (player == Resources.PLAYER.PLAYER2)
                        eventCollection.RegisterForEvent("uimouse", strEventReceiver);
                    break;
                default:
                 break;
	        }

            // Paddle needs to register 

        }
    }
}
